package com.yanqu.road.server.manger.activity.able;

import com.yanqu.road.entity.activity.ActivityInfo;
import com.yanqu.road.entity.activity.able.ActivityAbleInfo;
import com.yanqu.road.entity.activity.able.UserAbleData;
import com.yanqu.road.entity.activity.able.UserAbleItem;
import com.yanqu.road.entity.config.goods.GoodsInfo;
import com.yanqu.road.entity.enums.eLogMoneyType;
import com.yanqu.road.entity.enums.eMailType;
import com.yanqu.road.entity.player.UserInfo;
import com.yanqu.road.logic.bussiness.player.UserAbleBussiness;
import com.yanqu.road.logic.pb.BagPb;
import com.yanqu.road.pb.bag.BagProto;
import com.yanqu.road.server.TempMgr;
import com.yanqu.road.server.gameplayer.GamePlayer;
import com.yanqu.road.server.gameplayer.module.activity.able.ActivityAbleModule;
import com.yanqu.road.server.logic.mail.MailManager;
import com.yanqu.road.server.manger.MultipleLanguageMgr;
import com.yanqu.road.server.manger.ServerLanguageMgr;
import com.yanqu.road.server.manger.config.GoodsMgr;
import com.yanqu.road.server.manger.log.LogMgr;
import com.yanqu.road.server.manger.player.UserMgr;
import com.yanqu.road.server.manger.player.GamePlayerMgr;
import com.yanqu.road.server.manger.plugin.UserActivityAbleListener;
import com.yanqu.road.server.protocol.Protocol;
import com.yanqu.road.utils.date.DateHelper;
import com.yanqu.road.utils.string.StringUtils;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 活动凭证
 * <p>
 * 调用addUserActivityAbleListener添加新类型
 */
public class ActivityAbleMgr extends TempMgr {

    /**
     * 锁
     */
    private static final Map<Long, Object> lockMap = new ConcurrentHashMap<>();

    /**
     * 道具Map    Map<玩家ID，道具ID，数据>
     */
    private static Map<Long, Map<Integer, UserAbleItem>> userAbleItemMap = new ConcurrentHashMap<>();

    /**
     * 活动有资格Map    Map<玩家ID，活动ID，数据>
     */
    private static Map<Long, Map<Integer, UserAbleData>> userAbleDataMap = new ConcurrentHashMap<>();

    /**
     * 需要凭证的活动    Map<活动类型，活动ID，活动凭证信息>
     */
    private static Map<Integer, Map<Integer, ActivityAbleInfo>> activityAbleInfoMap = new ConcurrentHashMap<>();

    /**
     * 凭证监听器    Map<活动类型，监听>
     */
    private static Map<Integer, UserActivityAbleListener> activityAbleListenerMap = new ConcurrentHashMap<>();

    // ==========================================【锁】============================================

    /**
     * 获取玩家锁
     */
    private static Object getLockObject(long userId) {
        if (!lockMap.containsKey(userId)) {
            synchronized (lockMap) {
                if (!lockMap.containsKey(userId)) {
                    lockMap.put(userId, new Object());
                }
            }
        }
        return lockMap.get(userId);
    }

    // ========================================【凭证道具】======================================

    /**
     * 添加凭证道具
     */
    public static void addAbleItem(long userId, int itemId, long count, eLogMoneyType son) {
        if (count <= 0) {
            getLogger().debug("able item add count should more than zero! userId = {}, itemId = {}, count = {}, type = {}",
                    userId, itemId, count, son.getValue());
            return;
        }

        changeAbleItem(userId, itemId, true, count, son);

        // 尝试消耗凭证
        tryAbleActivityByItemId(userId, itemId);
    }

    /**
     * 扣除凭证道具
     */
    public static boolean removeAbleItem(long userId, int itemId, long count) {
        if (count <= 0) {
            getLogger().debug("remove able item count should more than zero! userId = {}, itemId = {}", userId, itemId);
            return false;
        }

        return changeAbleItem(userId, itemId, false, count, eLogMoneyType.ConsumeAbleItem);
    }

    /**
     * 变更凭证道具数量
     *
     * @return 是否成功
     */
    private static boolean changeAbleItem(long userId, int itemId, boolean get, long changeCount, eLogMoneyType son) {

        synchronized (getLockObject(userId)) {
            Map<Integer, UserAbleItem> itemMap = userAbleItemMap.get(userId);
            if (itemMap == null) {
                userAbleItemMap.put(userId, new ConcurrentHashMap<>());
                itemMap = userAbleItemMap.get(userId);
            }

            UserAbleItem item = itemMap.get(itemId);
            if (item == null) {
                item = new UserAbleItem(userId, itemId, 0, System.currentTimeMillis());
                item.setInsertOption();
                itemMap.put(itemId, item);
            }

            long oldCount = item.getNum();
            long newCount = get ? oldCount + changeCount : oldCount - changeCount;

            if (!get && newCount < 0) {
                return false; // 不够扣
            }

            item.setNum(newCount);
            item.setModifyTime(System.currentTimeMillis());

            // 日志
            long vipExp = 0;
            UserInfo userInfo = UserMgr.getUserInfo(userId);
            if (userInfo != null) {
                vipExp = userInfo.getVipExp();
            }
            LogMgr.addLogGoods(itemId, userId, get, changeCount, item.getNum(), son,vipExp);

            // 通过背包协议同步
            GamePlayer onlinePlayer = GamePlayerMgr.getOnlinePlayer(userId);
            if (onlinePlayer != null) {
                List<UserAbleItem> list = new ArrayList<>();
                list.add(item);
                BagProto.BagRespMsg.Builder builder = BagProto.BagRespMsg.newBuilder();
                BagPb.mergeAbleItem(builder, list);
                onlinePlayer.sendPacket(Protocol.U_BAG_SYNC_DATA, builder);
            }

        }

        return true;
    }

    /**
     * 获取所有凭证道具
     */
    public static List<UserAbleItem> getUserAbleItemList(long userId) {
        Map<Integer, UserAbleItem> map = userAbleItemMap.get(userId);
        if (map == null) return new ArrayList<>();
        return new ArrayList<>(map.values());
    }

    // ========================================【资格】======================================

    /**
     * 活动消耗凭证
     */
    public static void tryAbleActivity(ActivityAbleInfo ableInfo) {
        if (inAbleTime(ableInfo.getActivityInfo())) {
            for (long userId : userAbleItemMap.keySet()) {
                tryAbleUser(userId, ableInfo);
            }
        }
    }

    /**
     * 消耗凭证获取资格
     */
    private static void tryAbleUser(long userId, ActivityAbleInfo ableInfo) {
        // 时间判断
        if (inAbleTime(ableInfo.getActivityInfo())) {
            int activityId = ableInfo.getActivityInfo().getActivityId();
            int itemId = ableInfo.getAbleItemId();
            if (itemId == 0) {
                return; // 无需资格
            }
            GoodsInfo goodsInfo = GoodsMgr.getGoodsById(itemId);
            if (goodsInfo != null) {
                synchronized (getLockObject(userId)) {
                    if (isAble(userId, activityId)) {
                        return; // 已经有凭证
                    }

                    // 扣道具
                    long count = 1L;
                    if (!removeAbleItem(userId, itemId, count)) {
                        return; // 道具不够
                    }

                    // 添加凭证
                    UserAbleData userAbleData = new UserAbleData();
                    userAbleData.setUserId(userId);
                    userAbleData.setActivityId(activityId);
                    userAbleData.setAbleItemId(itemId);
                    userAbleData.setAbleTime(new Date());
                    userAbleData.setInsertOption();
                    if (!userAbleDataMap.containsKey(userId)) {
                        userAbleDataMap.put(userId, new ConcurrentHashMap<>());
                    }
                    userAbleDataMap.get(userId).put(activityId, userAbleData);

                    // 发送邮件通知
                    String language = UserMgr.getLanguage(userId);
                    String itemName = MultipleLanguageMgr.getContent(goodsInfo.getGoodsName(), language);
                    String activityName = ServerLanguageMgr.getContent(ableInfo.getActivityInfo().getActivityName(), language);
                    String mailTitle = MultipleLanguageMgr.getContent(MailManager.AUTO_USE_ABLE_ITEM_TITLE, language);
                    String mailContent = StringUtils.format(MultipleLanguageMgr.getContent(MailManager.AUTO_USE_ABLE_ITEM_CONTENT, UserMgr.getLanguage(userId)),
                            itemName, count, activityName);
                    MailManager.sendMail(userId, eMailType.SYSTEM.getValue(), "", mailContent, mailTitle);

                    // 触发监听
                    triggerAbleListener(ableInfo, userId);
                }
            } else {
                getLogger().error("can not find goods {} ! skip able activity {}", itemId, activityId);
            }
        }
    }

    /**
     * 获取资格后的操作
     */
    private static void triggerAbleListener(ActivityAbleInfo ableInfo, long userId) {
        // 玩家在线：同步资格
        GamePlayer onlinePlayer = GamePlayerMgr.getOnlinePlayer(userId);
        if (onlinePlayer != null) {
            onlinePlayer.getModule(ActivityAbleModule.class).syncAllAble();
        }

        // 有资格后操作
        UserActivityAbleListener listener = activityAbleListenerMap.get(ableInfo.getActivityInfo().getType());
        if (listener != null) {
            listener.afterAble(ableInfo, userId);
        }
    }

    /**
     * 是否有资格
     */
    public static boolean isAble(long userId, int activityId) {
        for (Map<Integer, ActivityAbleInfo> ableInfoMap : activityAbleInfoMap.values()) {
            if (ableInfoMap.containsKey(activityId)) {
                ActivityAbleInfo ableInfo = ableInfoMap.get(activityId);
                if (ableInfo != null && ableInfo.getAbleItemId() == 0) {
                    return true; // 无需资格
                }
                break;
            }
        }

        if (!userAbleDataMap.containsKey(userId)) {
            return false;
        }
        return userAbleDataMap.get(userId).containsKey(activityId);
    }

    /**
     * 是否在消耗凭证时间内（活动开始前5分钟 - 活动领奖期前）
     */
    public static boolean inAbleTime(ActivityInfo info) {
        if (info == null) {
            return false;
        }
        long now = System.currentTimeMillis() / 1000;
        return info.getBeginTime() - 5 * DateHelper.MINUTE_SECONDS < now && now < info.getEndTime();
    }

    /**
     * 获取有资格的活动ID
     */
    public static List<Integer> getAbleActivityIdInShowTime(long userId) {
        Map<Integer, UserAbleData> ableDataMap = userAbleDataMap.get(userId);
        if (ableDataMap == null || ableDataMap.isEmpty()) {
            return new ArrayList<>();
        }

        List<Integer> ableIdList = new ArrayList<>();
        for (Map<Integer, ActivityAbleInfo> infoMap : activityAbleInfoMap.values()) {
            for (ActivityAbleInfo ableInfo : infoMap.values()) {
                int activityId = ableInfo.getActivityInfo().getActivityId();
                if (ableDataMap.containsKey(activityId)) {
                    ableIdList.add(activityId);
                }
            }
        }
        return ableIdList;
    }

    // ========================================【监听器】======================================

    /**
     * 添加资格监听
     *
     * @param activityType 活动类型
     * @param listener     监听器
     * @param ableInfoMap  活动资格配置信息
     */
    public static void addActivityAbleListener(int activityType, Map<Integer, ActivityAbleInfo> ableInfoMap, UserActivityAbleListener listener) {
        activityAbleListenerMap.put(activityType, listener);
        activityAbleInfoMap.put(activityType, ableInfoMap);
        for (ActivityAbleInfo ableInfo : ableInfoMap.values()) {
            tryAbleActivity(ableInfo);
        }
    }

    /**
     * 消耗指定道具换资格
     */
    private static void tryAbleActivityByItemId(long userId, int itemId) {
        for (Map<Integer, ActivityAbleInfo> infoMap : activityAbleInfoMap.values()) {
            for (ActivityAbleInfo ableInfo : infoMap.values()) {
                if (ableInfo.getAbleItemId() == itemId) {
                    tryAbleUser(userId, ableInfo);
                }
            }
        }
    }

    /**
     * 定时器 消耗凭证换资格
     */
    public static void timerAbleActivity() {
        for (Map<Integer, ActivityAbleInfo> ableInfoMap : activityAbleInfoMap.values()) {
            for (ActivityAbleInfo ableInfo : ableInfoMap.values()) {
                ActivityInfo activityInfo = ableInfo.getActivityInfo();
                if (inAbleTime(activityInfo)) {
                    tryAbleActivity(ableInfo);
                }
            }
        }
    }

    // =========================================================================================

    @Override
    public boolean init() throws Exception {
        return reload();
    }

    @Override
    public boolean reloadConfig() throws Exception {
        return true;
    }

    @Override
    public boolean reloadData() throws Exception {
        userAbleDataMap = UserAbleBussiness.getUserAbleDataMap();
        userAbleItemMap = UserAbleBussiness.getUserAbleItemMap();
        return true;
    }

    @Override
    public boolean stop() {
        return true;
    }

    @Override
    public boolean save() {
        for (Map<Integer, UserAbleItem> map : userAbleItemMap.values()) {
            for (UserAbleItem item : map.values()) {
                if (item.isInsertOption()) {
                    UserAbleBussiness.addUserAbleItem(item);
                } else if (item.isUpdateOption()) {
                    UserAbleBussiness.updateUserAbleItem(item);
                }
            }
        }
        for (Map<Integer, UserAbleData> map : userAbleDataMap.values()) {
            for (UserAbleData data : map.values()) {
                if (data.isInsertOption()) {
                    UserAbleBussiness.addUserAbleData(data);
                } else if (data.isUpdateOption()) {
                    UserAbleBussiness.updateUserAbleData(data);
                }
            }
        }
        return true;
    }
}
